﻿using System;
using System.Collections.Generic;
using System.Threading;
using SDT.LogSystem;

namespace SDT.ServiceSystem
{
    /// <summary>
    /// Written by SinhNT
    /// </summary>
    public sealed class RunnerThread : IRunnerThread
    {
        public static readonly int Interval = 60000;

        private FileLog Logger = new FileLog();

        private List<IWorkerThread> Workers = new List<IWorkerThread>();
        private List<int> Counters = new List<int>();

        #region Runner Thread
        private AutoResetEvent signaller = new AutoResetEvent(true);
        private bool RunningState = true;

        /// <summary>
        /// Runner thread. This thread will be executed undefinitely.
        /// </summary>
        private Thread TheThread = null;

        private void ThreadMethod()
        {
            // Loop undefinitely
            while (true)
            {
                lock (signaller)
                {
                    if (!RunningState)
                        signaller.WaitOne(-1);
                }
                lock (Workers)
                {
                    for (int i = 0; i < Workers.Count; i++)
                    {
                        Counters[i] += Interval;
                        if (Counters[i] >= Workers[i].Interval)
                        {
                            Logger.AddEntry(string.Format("[{0}].StartWorking() @{1}", Workers[i].Id, Counters[i]), 1);
                            Workers[i].StartWorking();
                            Counters[i] = 0;
                        }
                    }
                }
                Thread.Sleep(Interval);
            }
        } 
        #endregion

        public RunnerThread()
        {
            // Create runner-thread
            TheThread = new Thread(new ThreadStart(ThreadMethod));
            TheThread.IsBackground = false;
        }

        #region IRunnerThread members
        public void RegisterWorker(IWorkerThread aWorker)
        {
            lock (Workers)
            {
                foreach (var item in Workers)
                    if (item.Id == aWorker.Id)
                        throw new NotSupportedException();

                Logger.AddEntry(string.Format("Register [{0}]", aWorker.Id), 1);

                Workers.Add(aWorker);
                Counters.Add(0);
            }
        }

        public void UnregisterWorker(IWorkerThread aWorker)
        {
            lock (Workers)
            {
                for (int i = 0; i < Workers.Count; i++)
                {
                    if (aWorker.Id == Workers[i].Id)
                    {
                        Logger.AddEntry(string.Format("Unregister [{0}]", Workers[i].Id), 1);
                        Counters.RemoveAt(i);
                        Workers.RemoveAt(i);
                    }
                }
            }
        }

        public void Start()
        {
            lock (TheThread)
            {
                Logger.AddEntry("Start Runner");
                Logger.AddEntry(string.Format("From state {0}", TheThread.ThreadState), 1);

                if (ThreadState.Unstarted == (TheThread.ThreadState & ThreadState.Unstarted))
                    TheThread.Start();  // Start it
                else if (ThreadState.WaitSleepJoin == (TheThread.ThreadState & ThreadState.WaitSleepJoin))
                    signaller.Set();    // Release from wait-state

                Thread.Sleep(100);
                Logger.AddEntry(string.Format("To state {0}", TheThread.ThreadState), 1);
            }
        }

        public bool Stop()
        {
            var ret = true;
            Logger.AddEntry("Stop runner");

            lock (signaller)
            {    
                foreach (var item in Workers)
                {
                    ret = item.StopWorking();
                    Logger.AddEntry(string.Format("[{0}].StopWorking() - {1}", item.Id, ret), 1);
                }
                if (ret)
                {
                    RunningState = false;
                    signaller.Reset();

                    Logger.AddEntry("Stop runner completely");
                }
            }
            return true;
        }
        #endregion
    }
}